

package search.fitnessfunctions;


import java.util.*;
import primitives.cluster.*;

public class DepthPenaltyMQ extends BunchTurboMQFitnessFunction{
	
	public double evaluate(ClusterHead ch){
		
		int depth = maxDepth(ch) -1;

		ClusterHead toEval = extendLayers(ch,depth);
			
		int level = 0;

		double total = 0;

		HashSet<IClusterLevel> current = new HashSet<IClusterLevel>();
		current.add(ch);
		
		
		int count = current.size();
		while(!current.isEmpty() && count > 0){
			HashSet<IClusterLevel> next = new HashSet<IClusterLevel>();
			ClusterHead temp = new ClusterHead(current, true);
		
			double fitness=0;
			
			count = 0;
			
			fitness = super.evaluate(temp);
			
	
			total += fitness;


			for(IClusterLevel c: current){

				for(IClusterLevel d: ((ClusterNode)c).getChildren()){
					if(d.encapsulates()){
						
						next.add(d);
						if( ((ClusterNode)d).getNodes().size() > 1){
							count++;
						}
					}
				}
			}
		
			current = next;
		}
		
		//total *= 100;
		

		
		if(depth > 4){
			//asympt: 1 .. \ifty
			double asympt = Math.pow(depth-3,2);
			double adjustment = 1.0/(asympt);
			total *= adjustment;
			total = Math.max(total, 0);
		}
		
		return total;
	}

	public static ClusterHead extendLayers(ClusterHead ch, int dp){
		ClusterHead copy= ch.deepCopy();
		
		//depth--;

		int depth = maxDepth(copy)-1; //need to sub 1 because level will be 1 more than the max value when in the inner loop below.


		List<IClusterLevel> current = new ArrayList<IClusterLevel>();
		current.add(ch);


		int count = 0;
		while(!current.isEmpty()){
			List<IClusterLevel> next = new ArrayList<IClusterLevel>();

			for(IClusterLevel c: current){

				HashSet<IClusterLevel> children = new HashSet<IClusterLevel>();
				children.addAll(((ClusterNode)c).getChildren() );			
				for(IClusterLevel d: children){
//					count++;
//					System.out.println(depth + " " + count);
					if(d.encapsulates()) next.add(d);
					else{
						if(depth > 0){

							ClusterNode parent = ClusterUtils.addEmptyIgnoreID((ClusterNode)c, copy);
							ClusterUtils.move(d,(ClusterNode) c, parent, copy, false);
							next.add(parent);
						}
					}
				}
			}
			depth--;
			current = next;
		}


		return copy;
	}

	public static int maxDepth(ClusterHead ch){
		
		int level = 0;

		List<IClusterLevel> current = new ArrayList<IClusterLevel>();
		current.add(ch);

		while(!current.isEmpty()){
			List<IClusterLevel> next = new ArrayList<IClusterLevel>();

			for(IClusterLevel c: current){

				for(IClusterLevel d: ((ClusterNode)c).getChildren()){
					if(d.encapsulates()) next.add(d);
				}
			}
			level++;
			current = next;
		}
		return level;

	}
}